package sceneGraph;

import java.util.*;
import javax.vecmath.*;

public class Scene extends SceneGraphNode {
	private List<Planet> planets;
	private List<Moon> moons;

	private Camera camera;

	private AbstractPlanet curPlanet;
	private Player player;
	private SkyBox skyBox;

	private List<TextObject> textObjects;

	private boolean portalsActivated;

	private int playerHeight = 0;

	public Scene() {
		planets = new ArrayList<Planet>();
		moons = new ArrayList<Moon>();
		camera = new Camera();
		skyBox = new SkyBox(camera);
		textObjects = new ArrayList<TextObject>();

		portalsActivated = false;
	}

	public Camera getCamera() {
		return camera;
	}

	public SkyBox getSkyBox() {
		return skyBox;
	}

	public void toggleMapMode() {
		camera.toggleMapMode();
	}

	public void moveForward() {
		if (camera.isMapMode()) {
			moveCameraForward();
		} else {
			movePlayerForward();
		}
	}

	public void moveBackward() {
		if (camera.isMapMode()) {
			moveCameraBackward();
		} else {
			movePlayerBackward();
		}
	}

	public void moveLeft() {
		if (camera.isMapMode()) {
			moveCameraLeft();
		} else {
			turnPlayerLeft();
		}
	}

	public void moveRight() {
		if (camera.isMapMode()) {
			moveCameraRight();
		} else {
			turnPlayerRight();
		}
	}

	/* Camera Move Commands */

	private void fixCameraPositionToDistance(float distance) {
		Vector3f newCameraVector = new Vector3f(camera.getPosition());
		newCameraVector.normalize();
		newCameraVector.scale(distance);
		camera.setPoisition(new Point3f(newCameraVector));
	}

	public void moveCameraBackward() {
		Vector3f oldDistance = new Vector3f(camera.getPosition());
		float distance = oldDistance.length();

		Vector3f downDirection = new Vector3f();
		downDirection.scale(-1, camera.getUpDirection());
		moveCameraByDirection(downDirection);

		fixCameraPositionToDistance(distance);
	}

	public void moveCameraForward() {
		Vector3f oldDistance = new Vector3f(camera.getPosition());
		float distance = oldDistance.length();

		moveCameraByDirection(camera.getUpDirection());

		fixCameraPositionToDistance(distance);

	}

	public void moveCameraLeft() {
		moveCameraByDirection(camera.getLeftDirection());
	}

	public void moveCameraRight() {
		Vector3f rightDirection = new Vector3f();
		rightDirection.scale(-1, camera.getLeftDirection());
		moveCameraByDirection(rightDirection);
	}

	public void zoomIn() {
		if (camera.isMapMode() && (camera.getAbsPosition().distance(curPlanet.getAbsPosition()) > curPlanet.getRadius() * 2)) {
			moveCameraByDirection(camera.getDirection());
		}
	}

	public void zoomOut() {
		if (camera.isMapMode()) {
			Vector3f backDirection = new Vector3f();
			backDirection.scale(-1, camera.getDirection());
			moveCameraByDirection(backDirection);
		}
	}

	public void moveCameraByDirection(Vector3f moveDirection) {
		// Calculate the new camera position
		Point3f newCameraPos = new Point3f(camera.getPosition());
		newCameraPos.add(moveDirection);

		camera.setPoisition(newCameraPos);
	}

	/* Player Move Commands */

	public void movePlayerForward() {
		movePlayerByDirection(player.getDirection());
	}

	public void movePlayerBackward() {
		Vector3f downDirection = new Vector3f();
		downDirection.scale(-1, player.getDirection());
		movePlayerByDirection(downDirection);
	}

	public void turnPlayerRight() {
		player.turnRight();
	}

	public void turnPlayerLeft() {
		player.turnLeft();
	}

	public void movePlayerByDirection(Vector3f moveDirection) {
		// Calculate the new player position
		Point3f newPlayerPos = new Point3f(player.getPosition());
		moveDirection.scale(0.1f);
		newPlayerPos.add(moveDirection);

		curPlanet.setPlayerPosition(newPlayerPos);
	}

	public void playerJump() {
		if (!camera.isMapMode() && playerHeight == 0) {
			playerHeight = 1;
		}
	}

	public void refreshPlayer() {
		if (playerHeight > 0) {
			playerHeight++;
		}
		if (playerHeight < 0) {
			playerHeight++;
		}
		if (playerHeight == 10) {
			playerHeight = -10;
		}

		curPlanet.setPlayerHeight(playerHeight);
		curPlanet.setPlayerPosition(player.getPosition());
	}

	public List<Planet> getPlanets() {
		return planets;
	}

	public List<Moon> getMoons() {
		return moons;
	}

	public List<AbstractPlanet> getPlanetsAndMoons() {
		List<AbstractPlanet> allPlanets = new ArrayList<AbstractPlanet>();
		allPlanets.addAll(planets);
		allPlanets.addAll(moons);
		return allPlanets;
	}

	@Override
	public void accept(Visitor visitor) {
		visitor.visit(this);
	}

	public void addPlanet(Planet p) {
		planets.add(p);
	}

	public void addMoon(Moon m) {
		moons.add(m);
	}

	public void addTextObject(TextObject t) {
		textObjects.add(t);
	}

	public void removeTextObject(TextObject t) {
		textObjects.remove(t);
	}

	public List<TextObject> getTextObjects() {
		return textObjects;
	}

	public void setPlayer(Player player, AbstractPlanet planet) {

		if (curPlanet != null) {
			curPlanet.removePlayer();
		}
		this.player = player;
		this.curPlanet = planet;
		planet.setPlayer(player);
		camera.setPlayer(player, curPlanet);

		curPlanet.reMesh(4);
		for (AbstractPlanet p : getPlanetsAndMoons()) {
			if (p != curPlanet) {
				p.reMesh(3);
			}
		}
	}

	public AbstractPlanet getCurrentPlanet() {
		return curPlanet;
	}

	public AbstractPlanetObject getIntersectedObject() {
		return curPlanet.getIntersectedObject();
	}

	public void activatePortals(boolean mode) {
		portalsActivated = mode;
	}

	public boolean handlePortal(Portal portal) {
		if (portalsActivated) {
			if (portal.getDestination() != null) {
				setPlayer(player, portal.getDestination());
			}
			return true;
		}
		return false;
	}

	public int getPlayerHeight() {
		return playerHeight;
	}
}
